from typing import List, Dict, Any, Optional, Callable
from datetime import timeout
from utils.logger import Logger
from utils.exceptions import ToolError, ParamError
from middleware.redis_adapter import get_redis_adapter
from .api_client import APIClient  # 复用已有的API客户端

# 初始化日志
logger = Logger.get_logger(agent_name="tool_call_tool")

# 工具注册中心（存储工具元信息：名称→{函数、参数列表、描述}）
TOOL_REGISTRY: Dict[str, Dict[str, Any]] = {}


def register_tool(name: str, description: str, params: List[Dict[str, Any]]) -> Callable:
    """工具注册装饰器（用于注册自定义工具）"""

    def decorator(func: Callable) -> Callable:
        TOOL_REGISTRY[name] = {
            "func": func,
            "description": description,
            "params": params  # 参数规范：[{"name": "param1", "type": "str", "required": True, "desc": "..."}]
        }
        logger.info(f"工具注册成功: name={name}, params={[p['name'] for p in params]}")
        return func

    return decorator


class ToolCallTools:
    """工具调用核心工具集：工具注册、参数验证、执行、结果格式化"""

    def __init__(self):
        """初始化：API客户端、Redis缓存（工具执行结果缓存）"""
        try:
            self.api_client = APIClient()
            self.redis_client = get_redis_adapter()  # 使用Redis适配器，支持集群模式
            self.tool_cache_ttl = 3600  # 工具执行结果缓存1小时（适用于幂等工具）
            # 自动注册内置工具
            self._register_builtin_tools()
            logger.info("ToolCallTools initialized successfully")
        except Exception as e:
            logger.error(f"ToolCallTools初始化失败: {str(e)}", exc_info=True)
            raise ToolError(
                message="工具调用工具初始化失败",
                context={"error": str(e)}
            ) from e

    def _register_builtin_tools(self):
        """注册内置工具（天气查询、数学计算、网页搜索）"""

        # 1. 天气查询工具
        @register_tool(
            name="weather_query",
            description="查询指定城市的实时天气",
            params=[
                {"name": "city", "type": "str", "required": True, "desc": "城市名称（如：北京、上海）"},
                {"name": "lang", "type": "str", "required": False, "desc": "语言（zh/cn，默认zh）"}
            ]
        )
        def weather_query(city: str, lang: str = "zh") -> Dict[str, Any]:
            """调用天气API查询实时天气（使用高德地图API示例）"""
            try:
                # 缓存键（幂等操作，可缓存）
                cache_key = f"tool:weather:{city}:{lang}"
                cached_result = self.redis_client.get(cache_key)
                if cached_result:
                    logger.debug(f"从缓存获取天气结果: city={city}")
                    return eval(cached_result.decode("utf-8"))  # 简化：实际用json.loads

                # 调用第三方天气API（替换为真实API密钥）
                api_key = os.getenv("AMAP_WEATHER_KEY", "your_api_key")
                url = f"https://restapi.amap.com/v3/weather/weatherInfo?city={city}&key={api_key}&extensions=base&output=json"
                response = self.api_client.send_request(url=url, method="GET")

                if response.get("status") != "1":
                    raise ToolError(message=f"天气查询失败: {response.get('info', '未知错误')}")

                # 格式化结果
                weather_data = response["lives"][0]
                result = {
                    "city": weather_data["city"],
                    "temperature": weather_data["temperature"],
                    "weather": weather_data["weather"],
                    "wind_direction": weather_data["winddirection"],
                    "humidity": weather_data["humidity"],
                    "report_time": weather_data["reporttime"]
                }

                # 写入缓存
                self.redis_client.set(cache_key, str(result), ex=self.tool_cache_ttl)
                return result
            except Exception as e:
                raise ToolError(message=f"天气查询失败: {str(e)}") from e

        # 2. 数学计算工具
        @register_tool(
            name="math_calculator",
            description="执行数学计算（支持加减乘除、幂运算、三角函数等）",
            params=[
                {"name": "expression", "type": "str", "required": True, "desc": "数学表达式（如：1+2*3、sin(30)、sqrt(16)）"}
            ]
        )
        def math_calculator(expression: str) -> Dict[str, Any]:
            """执行数学计算（使用eval，实际场景需加安全过滤）"""
            try:
                # 安全过滤：仅允许数学相关函数和运算符
                allowed_chars = "0123456789+-*/()[]{}.,sin cos tan log sqrt pow abs round"
                for char in expression:
                    if char not in allowed_chars and not char.isspace():
                        raise ParamError(message=f"不允许的字符: {char}")

                # 执行计算
                import math
                local_vars = {"math": math}
                result = eval(expression, {"__builtins__": {}}, local_vars)  # 限制内置函数

                return {
                    "expression": expression,
                    "result": float(result),
                    "status": "success"
                }
            except ParamError:
                raise
            except Exception as e:
                raise ToolError(message=f"数学计算失败: {str(e)}") from e

        # 3. 网页搜索工具（模拟）
        @register_tool(
            name="web_search",
            description="搜索网页内容（返回相关结果摘要）",
            params=[
                {"name": "query", "type": "str", "required": True, "desc": "搜索关键词"},
                {"name": "top_k", "type": "int", "required": False, "desc": "返回结果数（默认3）"}
            ]
        )
        def web_search(query: str, top_k: int = 3) -> Dict[str, Any]:
            """模拟网页搜索（实际调用搜索引擎API）"""
            try:
                # 缓存键
                cache_key = f"tool:web_search:{query}:{top_k}"
                cached_result = self.redis_client.get(cache_key)
                if cached_result:
                    logger.debug(f"从缓存获取搜索结果: query={query}")
                    return eval(cached_result.decode("utf-8"))

                # 模拟搜索结果（实际替换为Google/Baidu API）
                results = [
                    {"title": f"结果{idx + 1}: {query} - 百科", "summary": f"这是{query}的相关百科摘要...",
                     "url": f"https://example.com/search/{idx + 1}"}
                    for idx in range(top_k)
                ]

                result = {
                    "query": query,
                    "top_k": top_k,
                    "results": results
                }

                # 写入缓存
                self.redis_client.set(cache_key, str(result), ex=self.tool_cache_ttl)
                return result
            except Exception as e:
                raise ToolError(message=f"网页搜索失败: {str(e)}") from e

    def get_tool_list(self) -> List[Dict[str, Any]]:
        """获取所有已注册工具的元信息（用于Agent选择工具）"""
        tool_list = []
        for name, info in TOOL_REGISTRY.items():
            tool_list.append({
                "name": name,
                "description": info["description"],
                "params": info["params"]
            })
        return tool_list

    def validate_tool_params(self, tool_name: str, params: Dict[str, Any]) -> None:
        """验证工具参数是否符合规范"""
        if tool_name not in TOOL_REGISTRY:
            raise ToolError(message=f"未注册的工具: {tool_name}")

        tool_info = TOOL_REGISTRY[tool_name]
        required_params = [p["name"] for p in tool_info["params"] if p["required"]]

        # 检查必填参数
        missing_params = [p for p in required_params if p not in params]
        if missing_params:
            raise ParamError(message=f"缺少必填参数: {missing_params}")

        # 检查参数类型（简化：仅校验基础类型）
        for param_spec in tool_info["params"]:
            param_name = param_spec["name"]
            if param_name not in params:
                continue
            expected_type = param_spec["type"]
            actual_value = params[param_name]
            actual_type = type(actual_value).__name__

            # 类型转换（如：字符串转整数）
            if expected_type == "int" and actual_type == "str" and actual_value.isdigit():
                params[param_name] = int(actual_value)
            elif expected_type == "float" and actual_type == "str" and actual_value.replace(".", "").isdigit():
                params[param_name] = float(actual_value)
            elif expected_type != actual_type and not (expected_type == "int" and actual_type == "float"):
                raise ParamError(
                    message=f"参数类型错误: {param_name} 期望{expected_type}，实际{actual_type}"
                )

    def call_tool(self, tool_name: str, params: Dict[str, Any], timeout: int = 10) -> Dict[str, Any]:
        """调用指定工具（参数验证→执行→结果格式化）"""
        try:
            logger.debug(f"开始调用工具: tool_name={tool_name}, params={params}")

            # 1. 参数验证
            self.validate_tool_params(tool_name, params)

            # 2. 获取工具函数
            tool_func = TOOL_REGISTRY[tool_name]["func"]

            # 3. 执行工具（带超时控制）
            import threading
            result = None
            error = None

            def tool_executor():
                nonlocal result, error
                try:
                    result = tool_func(**params)
                except Exception as e:
                    error = e

            thread = threading.Thread(target=tool_executor)
            thread.start()
            thread.join(timeout=timeout)

            if thread.is_alive():
                raise ToolError(message=f"工具调用超时（{timeout}秒）: {tool_name}")
            if error:
                raise error

            # 4. 格式化结果
            formatted_result = {
                "tool_name": tool_name,
                "params": params,
                "status": "success",
                "data": result,
                "error_msg": ""
            }

            logger.debug(f"工具调用成功: tool_name={tool_name}, result={str(result)[:100]}")
            return formatted_result
        except (ToolError, ParamError):
            raise
        except Exception as e:
            logger.error(f"工具调用失败: tool_name={tool_name}, params={params}, error={str(e)}", exc_info=True)
            raise ToolError(
                message=f"工具调用失败: {str(e)}",
                context={"tool_name": tool_name, "params": params}
            ) from e


# 单例工具实例
tool_call_tools = ToolCallTools()

# 测试入口
if __name__ == "__main__":
    test_logger = Logger.update_context(task_id="tool_call_test", agent_name="tool_call_tool")
    test_logger.info("开始测试工具调用工具")

    try:
        # 1. 获取工具列表
        tool_list = tool_call_tools.get_tool_list()
        test_logger.info(f"已注册工具: {[tool['name'] for tool in tool_list]}")

        # 2. 调用天气查询工具
        weather_params = {"city": "北京", "lang": "zh"}
        weather_result = tool_call_tools.call_tool("weather_query", weather_params)
        test_logger.info(f"天气查询结果: {weather_result}")

        # 3. 调用数学计算工具
        math_params = {"expression": "1+2*3+math.sqrt(16)"}
        math_result = tool_call_tools.call_tool("math_calculator", math_params)
        test_logger.info(f"数学计算结果: {math_result}")

        # 4. 调用网页搜索工具
        search_params = {"query": "分布式多Agent系统", "top_k": 2}
        search_result = tool_call_tools.call_tool("web_search", search_params)
        test_logger.info(f"网页搜索结果: {search_result}")

        test_logger.success("工具调用工具测试完成")
    except (ToolError, ParamError) as e:
        test_logger.error(f"测试失败: {e}")